#include "GLSLShader.h"
#include "GL/glew.h"
#include "GL/wglew.h"

#include <iostream>
#include <fstream>

GLSLShader::GLSLShader(std::wstring vertexShader, std::wstring fragmentShader)
	:m_VertexShader(vertexShader),
	m_FragmentShader(fragmentShader)
{
}


GLSLShader::~GLSLShader(void)
{
}

void GLSLShader::Init()
{
	m_VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
	m_FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);

	std::string vertexSource = ReadAsciiFile(m_VertexShader);
	GLint vlength = vertexSource.length();
	const GLchar* vertexCharSource = vertexSource.c_str();
	glShaderSource(m_VertexShaderID, 1 , &vertexCharSource, &vlength);
	
	std::string fragmentSource = ReadAsciiFile(m_FragmentShader);
	GLint flength = fragmentSource.length();
	const GLchar* fragmentCharSource = fragmentSource.c_str();
	glShaderSource(m_FragmentShaderID, 1 , &fragmentCharSource, &flength);

	glCompileShader(m_VertexShaderID);
	GLint vresult;
	glGetShaderiv(m_VertexShaderID, GL_COMPILE_STATUS,&vresult); 

	glCompileShader(m_FragmentShaderID);
	GLint fresult;
	glGetShaderiv(m_FragmentShaderID, GL_COMPILE_STATUS,&fresult); 

	if ( vresult == GL_TRUE && fresult == GL_TRUE )
	{
		m_ProgramID = glCreateProgram();
		glAttachShader(m_ProgramID,m_VertexShaderID);
		glAttachShader(m_ProgramID,m_FragmentShaderID);

		glLinkProgram(m_ProgramID);

		GLint presult;
		glGetProgramiv(m_ProgramID,GL_LINK_STATUS, &presult);
		if (presult == GL_FALSE )
		{
			std::cout<<"compilation was unsuccesful";
		}
	}
	Activate();
	

	/*
	GLint transformLocation = glGetUniformBlockIndex(m_ProgramID,"Transformation");
	if ( transformLocation >-1 )
	{
		glGenBuffers(1, &m_TransformBlockID);
		glBindBuffer(GL_UNIFORM_BUFFER, m_TransformBlockID);
		glBufferData(GL_UNIFORM_BUFFER, 32*sizeof(float), NULL, GL_DYNAMIC_DRAW);

		GLint blockSize; 
		glGetActiveUniformBlockiv(m_ProgramID, transformLocation,
			GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);

		glBindBufferBase(GL_UNIFORM_BUFFER, 0, m_TransformBlockID);
		glUniformBlockBinding(m_ProgramID, transformLocation, 0);
	}
	*/

	m_WorldViewTransform = new Uniform(glGetUniformLocation(m_ProgramID,"projection_matrix"));
	m_ProjectionTransform = new Uniform(glGetUniformLocation(m_ProgramID,"modelview_matrix"));

	CreateVariables();
}

void GLSLShader::Activate()
{
	glUseProgram(m_ProgramID);
}

void GLSLShader::Deactivate()
{
	
}

void GLSLShader::SetTransforms()
{
	static float4x4 wvtransform;
	static float4x4 ptransform;
	glGetFloatv(GL_PROJECTION_MATRIX,ptransform.m_M);
	glGetFloatv(GL_MODELVIEW_MATRIX,wvtransform.m_M);
	
	*m_WorldViewTransform = wvtransform;
	*m_ProjectionTransform = ptransform;
	/*
	glBindBuffer(GL_UNIFORM_BUFFER,m_TransformBlockID);
	glBufferData(GL_UNIFORM_BUFFER, 32*sizeof(float), transform, GL_DYNAMIC_DRAW);
	*/
}

Uniform& GLSLShader::GetUniformVariable(std::string name)
{
	return *m_VariableMap[name];
}

void GLSLShader::AddUniformVariable(std::string name)
{
	GLint locID = glGetUniformLocation(m_ProgramID,name.c_str());
	m_VariableMap[name] = new Uniform(locID);
}

std::string GLSLShader::ReadAsciiFile(std::wstring filename)
{
	std::ifstream file;
	file.open(filename.c_str());
	std::string result = "";
	std::string line;
	if ( file.is_open())
	{
		while ( file.good() )
		{
			getline (file,line);
			result += line + "\n";
		}
		file.close();
	}
	return result;

}